package com.pj.ca.util;

import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfIndirectReference;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfImageObject;
import com.itextpdf.text.pdf.security.CertificateInfo;
import com.itextpdf.text.pdf.security.PdfPKCS7;
import com.pj.ca.modal.SignatureInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * 描述：
 *
 * @author pengjun
 * @date 2021/11/4 14:54
 */
public class VerifyItext5PdfUtil {
    public static final BouncyCastleProvider BOUNCYCASTLEPROVIDER = new BouncyCastleProvider();

    public static List<SignatureInfo> getSignatureInfoList(byte[] pdfArray) throws Exception {
        PdfReader reader = null;
        List<SignatureInfo> signatureInfoList = new ArrayList<>();
        Security.addProvider(BOUNCYCASTLEPROVIDER);
        try {
            reader = new PdfReader(pdfArray);
            AcroFields fields = reader.getAcroFields();
            ArrayList<String> signatureNames = fields.getSignatureNames();
            if (signatureNames == null || signatureNames.isEmpty()) {
                return signatureInfoList;
            }
            for (String signatureName : signatureNames) {
                SignatureInfo signatureInfo = new SignatureInfo();
                PdfPKCS7 pk = fields.verifySignature(signatureName,"BC");
                // 签约时间
                Date signDate = pk.getSignDate() == null ? null : pk.getSignDate().getTime();
                signatureInfo.setSignTime(signDate);

                X509Certificate cert = pk.getSigningCertificate();
                // 证书有效期开始
                Date notBefore = cert.getNotBefore();
                signatureInfo.setCertDateStart(notBefore);
                // 证书有效期结束
                Date notAfter = cert.getNotAfter();
                signatureInfo.setCertDateEnd(notAfter);
                //证书颁发机构
                CertificateInfo.X500Name issuerFields =CertificateInfo.getIssuerFields(cert);
                String organization =  issuerFields.getField("O");
                signatureInfo.setOrganization(organization);

                // 签约人
                String signatory = CertificateInfo.getSubjectFields(cert).getField("CN");
                signatureInfo.setSignatory(signatory);

                //获取签章图片
                String imageString = getPdfImageString(fields,signatureName);
                signatureInfo.setImgBase64String(imageString);

                // 签名校验结果
                if (pk.verify()) {
                    signatureInfo.setCode(0);
                    signatureInfo.setMessage(SignatureInfo.SUCCESS_MESSAGE);
                } else {
                    signatureInfo.setCode(1);
                    signatureInfo.setMessage(SignatureInfo.ERROR_MESSAGE);
                }

                // 时间戳设置
                if (pk.getTimeStampDate() != null) {
                    signatureInfo.setHasTimeStamp(true);
                    signatureInfo.setTimeStampMessage(SignatureInfo.HAS_TIME_STAMP_MESSAGE);
                } else {
                    signatureInfo.setHasTimeStamp(false);
                    signatureInfo.setTimeStampMessage(SignatureInfo.NO_TIME_STAMP_MESSAGE);
                }
                signatureInfoList.add(signatureInfo);
            }

        } catch (Exception e) {
            System.out.println(e);
            throw e;
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {

                }
            }
        }
        return signatureInfoList;
    }

    private static String getPdfImageString(AcroFields fields, String signatureName) throws Exception {
        //itext5读取印章图片
        AcroFields.Item fieldItem = fields.getFieldItem(signatureName);
        PdfDictionary pdfDictionary = fieldItem.getValue(0);
        PdfDictionary signatureDr = pdfDictionary.getAsDict(PdfName.DR);
        PdfDictionary signatureDrXobject =  signatureDr.getAsDict(PdfName.XOBJECT);
        PdfIndirectReference frm =  signatureDrXobject.getAsIndirectObject(PdfName.FRM);
        if(frm == null){
            throw new Exception("印章图片索引为空");
        }
        PdfDictionary tempFrm = (PdfDictionary) PdfReader.getPdfObject(frm);
        PdfDictionary pdfDictionary1 =tempFrm.getAsDict(PdfName.RESOURCES);
        PdfDictionary signatureDrXobject1 = pdfDictionary1.getAsDict(PdfName.XOBJECT);
        PdfIndirectReference N2 =  signatureDrXobject1.getAsIndirectObject(PdfName.N2);
        if(N2 == null){
            throw new Exception("印章图片索引为空");
        }
        PdfDictionary tempN2 = (PdfDictionary) PdfReader.getPdfObject(N2);
        PdfDictionary pdfDictionary2 =tempN2.getAsDict(PdfName.RESOURCES);
        PdfDictionary signatureDrXobject2 = pdfDictionary2.getAsDict(PdfName.XOBJECT);
        Set<PdfName> setPdfNames = signatureDrXobject2.getKeys();
        PdfName[] pdfNames = new PdfName[setPdfNames.size()];
        PdfName[] names = setPdfNames.toArray(pdfNames);
        if (names.length == 0) {
            throw new Exception("签章外观图片数据为空");
        }
        BufferedImage bufferedImageOne = null;
        BufferedImage bufferedImageTwo = null;
        for(PdfName name : names){
            PdfIndirectReference img = signatureDrXobject2.getAsIndirectObject(name);
            PRStream stream = (PRStream) PdfReader.getPdfObject(img);
            Set<PdfName> keys = stream.getKeys();
            PdfImageObject image = new PdfImageObject(stream);
            if(!keys.contains(PdfName.SMASK)){
                bufferedImageOne = image.getBufferedImage();
            }
            if(keys.contains(PdfName.SMASK)){
                bufferedImageTwo = image.getBufferedImage();
            }
        }
        BufferedImage imageBuffer = null;
        if(bufferedImageOne == null && bufferedImageTwo == null){
            throw new Exception("签章外观图片数据为空");
        }else if(bufferedImageOne != null && bufferedImageTwo == null){
            imageBuffer = bufferedImageOne;
        }else if(bufferedImageOne == null && bufferedImageTwo != null){
            imageBuffer = bufferedImageTwo;
        }else{
            imageBuffer = transferAlpha(bufferedImageOne,bufferedImageTwo);
        }
        //ByteArrayOutputStream 是不需要关闭流的，因为时内存读写流，不同于只想硬盘的流，它内部时使用字节数组读内存的
        //字节数据是他的成员变量，当不再使用，Java的垃圾回收机制会将它回收
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(imageBuffer, "png", byteArrayOutputStream);
        byte[] byteArray = byteArrayOutputStream.toByteArray();
        String base64String = Base64.getEncoder().encodeToString(byteArray);
        return "data:image/png;base64,"+base64String;
    }

    private static BufferedImage transferAlpha(BufferedImage bufferedImage0, BufferedImage bufferedImage1) {
        try{
            BufferedImage bufferedImage = new BufferedImage(bufferedImage0.getWidth(), bufferedImage0.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
            for (int j1 = bufferedImage.getMinY(); j1 < bufferedImage.getHeight(); j1++) {
                for (int j2 = bufferedImage.getMinX(); j2 < bufferedImage.getWidth(); j2++) {
                    int rgb = bufferedImage0.getRGB(j2, j1);
                    int R = (rgb & 0xff0000) >> 16;
                    int G = (rgb & 0xff00) >> 8;
                    int B = (rgb & 0xff);
                    if (!(((255 - R) > 200) && ((255 - G) > 200) && ((255 - B) > 200))) {
                        bufferedImage.setRGB(j2, j1, bufferedImage1.getRGB(j2, j1));
                    }
                }
            }
            return bufferedImage;
        } catch (Exception e) {
            return null;
        }
    }
}
